package com.jzo2o.orders.manager.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.db.DbRuntimeException;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.customer.dto.response.AddressBookResDTO;
import com.jzo2o.api.foundations.ServeApi;
import com.jzo2o.api.foundations.dto.response.ServeAggregationResDTO;
import com.jzo2o.api.market.dto.request.CouponUseReqDTO;
import com.jzo2o.api.market.dto.response.AvailableCouponsResDTO;
import com.jzo2o.api.market.dto.response.CouponUseResDTO;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.model.msg.TradeStatusMsg;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.NumberUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.orders.base.config.OrderStateMachine;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusChangeEventEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO;
import com.jzo2o.orders.manager.model.dto.request.OrdersPayReqDTO;
import com.jzo2o.orders.manager.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.orders.manager.porperties.TradeProperties;
import com.jzo2o.orders.manager.service.IOrdersCreateService;
import com.jzo2o.orders.manager.service.cllient.CustomerClient;
import com.jzo2o.orders.manager.service.cllient.MarketClient;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

import static com.jzo2o.orders.base.constants.RedisConstants.Lock.ORDERS_SHARD_KEY_ID_GENERATOR;

/**
 * <p>
 * 下单服务类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersCreateServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersCreateService {

    @Resource
    private CustomerClient customerClient;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private ServeApi serveApi;
    @Resource
    private OrdersCreateServiceImpl owner;
    @Resource
    private NativePayApi nativePayApi;
    @Resource
    private TradeProperties tradeProperties;
    @Resource
    private TradingApi tradingApi;
    @Resource
    private OrderStateMachine orderStateMachine;
    @Resource
    private MarketClient marketClient;
    /**
     * 生成订单id 格式：{yyMMdd}{13位id}
     *
     * @return
     */
    private Long generateOrderId() {
        //通过Redis自增序列得到序号
        Long id = redisTemplate.opsForValue().increment(ORDERS_SHARD_KEY_ID_GENERATOR, 1);
        //生成订单号   2位年+2位月+2位日+13位序号
        long orderId = DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd") * 10000000000000L + id;
        return orderId;
    }

    @Override
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO) {
        //需要远程调用jzo2o-customer服务查询地址簿信息
        AddressBookResDTO detail = customerClient.getDetail(placeOrderReqDTO.getAddressBookId());
        if (detail == null) {
            throw new BadRequestException("预约地址异常，无法下单");
        }
        //远程调用foundation服务查询服务相关信息
        ServeAggregationResDTO serveResDTO = serveApi.findById(placeOrderReqDTO.getServeId());
        //服务下架不可下单
        if (serveResDTO == null || serveResDTO.getSaleStatus() != 2) {
            throw new BadRequestException("服务不可用");
        }
        // 2.下单前数据准备
        Orders orders = new Orders();
        // id 订单id
        orders.setId(generateOrderId());
        // userId，从threadLocal获取当前登录用户的id，通过UserContextInteceptor拦截进行设置
        orders.setUserId(UserContext.currentUserId());
        // 服务id
        orders.setServeId(placeOrderReqDTO.getServeId());
        // 服务项id
        orders.setServeItemId(serveResDTO.getServeItemId());
        orders.setServeItemName(serveResDTO.getServeItemName());
        orders.setServeItemImg(serveResDTO.getServeItemImg());
        orders.setUnit(serveResDTO.getUnit());
        //服务类型信息
        orders.setServeTypeId(serveResDTO.getServeTypeId());
        orders.setServeTypeName(serveResDTO.getServeTypeName());
        // 订单状态
        orders.setOrdersStatus(0);
        // 支付状态，暂不支持，初始化一个空状态
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        // 服务时间
        orders.setServeStartTime(placeOrderReqDTO.getServeStartTime());
        // 城市编码
        orders.setCityCode(serveResDTO.getCityCode());
        // 地理位置
        orders.setLon(detail.getLon());
        orders.setLat(detail.getLat());

        String serveAddress = new StringBuffer(detail.getProvince())
                .append(detail.getCity())
                .append(detail.getCounty())
                .append(detail.getAddress())
                .toString();
        orders.setServeAddress(serveAddress);
        // 联系人
        orders.setContactsName(detail.getName());
        orders.setContactsPhone(detail.getPhone());

        // 价格
        orders.setPrice(serveResDTO.getPrice());
        // 购买数量
        orders.setPurNum(NumberUtils.null2Default(placeOrderReqDTO.getPurNum(), 1));
        // 订单总金额 价格 * 购买数量
        orders.setTotalAmount(orders.getPrice().multiply(new BigDecimal(orders.getPurNum())));

        // 优惠金额 当前默认0
        orders.setDiscountAmount(BigDecimal.ZERO);
        // 实付金额 订单总金额 - 优惠金额
        orders.setRealPayAmount(NumberUtils.sub(orders.getTotalAmount(), orders.getDiscountAmount()));
        //排序字段,根据服务开始时间转为毫秒时间戳+订单后5位
        long sortBy = DateUtils.toEpochMilli(orders.getServeStartTime()) + orders.getId() % 100000;
        orders.setSortBy(sortBy);
        //判断如果使用了优惠券进行核销
        if(ObjectUtils.isNotNull(placeOrderReqDTO.getCouponId())){
            //进行核销
            owner.addWithCoupon(orders,placeOrderReqDTO.getCouponId());
        }else {
            //保存订单
            owner.add(orders);
        }
        return new PlaceOrderResDTO(orders.getId());
    }

    /*
    * 当下单是有有优惠券，调用此方法
    * */
    //@Transactional(rollbackFor = Exception.class)
    @GlobalTransactional//开启全局事务
    public void addWithCoupon(Orders orders,Long couponId){
        //远程调用优惠券服务核销
        CouponUseReqDTO couponUseReqDTO = new CouponUseReqDTO();
        couponUseReqDTO.setId(couponId);
        couponUseReqDTO.setOrdersId(orders.getId());
        couponUseReqDTO.setTotalAmount(orders.getTotalAmount());

        //核销成功返回优惠金额
        CouponUseResDTO couponUseResDTO = marketClient.use(couponUseReqDTO);
        //优惠价格
        orders.setDiscountAmount(couponUseResDTO.getDiscountAmount());
        // 实付金额 订单总金额 - 优惠金额
        orders.setRealPayAmount(NumberUtils.sub(orders.getTotalAmount(), orders.getDiscountAmount()));
        //保存订单信息
        add(orders);
    }

//    @Override
//    public OrdersPayResDTO pay(Long id, OrdersPayReqDTO ordersPayReqDTO) {
//        //查一下订单
//        Orders orders = baseMapper.selectById(id);
//        //不存在就抛出异常
//        if (ObjectUtils.isNull(orders)){
//            throw new CommonException("订单不存在");
//        }
//        //存在已经支付成功直接返回
//        Integer payStatus = orders.getPayStatus();//支付状态
//        if (payStatus==OrderPayStatusEnum.PAY_SUCCESS.getStatus()&&ObjectUtils.isNotEmpty(orders.getTradingOrderNo())){
//            OrdersPayResDTO ordersPayResDTO = new OrdersPayResDTO();
//            BeanUtil.copyProperties(orders, ordersPayResDTO);
//            ordersPayResDTO.setProductOrderNo(orders.getId());
//            return ordersPayResDTO;
//        }else{
//            //请求支付服务生成二维码
//            NativePayResDTO nativePayResDTO = generateQrCode(orders, ordersPayReqDTO.getTradingChannel());
//            OrdersPayResDTO ordersPayResDTO = BeanUtils.toBean(nativePayResDTO, OrdersPayResDTO.class);
//
//            //将二维码更新到交易订单中
//            boolean update = lambdaUpdate()
//                    .eq(Orders::getId,id)
//                    .set(Orders::getTradingOrderNo, nativePayResDTO.getTradingOrderNo())
//                    .set(Orders::getTradingChannel, nativePayResDTO.getTradingChannel())
//                    .update();
//            if(!update){
//                throw new CommonException("订单:"+orders.getId()+"请求支付更新交易单号失败");
//            }
//
//            return ordersPayResDTO;
//        }
//
//    }
//
//    //请求支付服务生成二维码
//    private NativePayResDTO generateQrCode(Orders orders, PayChannelEnum tradingChannel) {
//        //封装请求支付服务的参数
//        NativePayReqDTO nativePayDTO = new NativePayReqDTO();
//        //根据请求的支付渠道判断需要使用哪个商户号
//        Long enterpriseId=ObjectUtils.equal(tradingChannel,PayChannelEnum.WECHAT_PAY)?tradeProperties.getWechatEnterpriseId():tradeProperties.getAliEnterpriseId();
//        nativePayDTO.setEnterpriseId(enterpriseId);
//        //家政的订单号
//        nativePayDTO.setProductOrderNo(orders.getId());
//        //支付金额
//        nativePayDTO.setTradingAmount(orders.getRealPayAmount());
//        //备注信息
//        nativePayDTO.setMemo(orders.getServeItemName());
//        //业务系统标识
//        nativePayDTO.setProductAppId("jzo2o.orders");
//        //支付渠道
//        nativePayDTO.setTradingChannel(tradingChannel);
//        //判断是否切换支付渠道
//        if (ObjectUtils.isNotEmpty(orders.getTradingChannel())
//                && ObjectUtils.notEqual(orders.getTradingChannel(), tradingChannel.getValue())) {
//            nativePayDTO.setChangeChannel(true);
//        }
//
//        //生成支付二维码
//        NativePayResDTO downLineTrading = nativePayApi.createDownLineTrading(nativePayDTO);
//        if(ObjectUtils.isNotNull(downLineTrading)){
//            log.info("订单:{}请求支付,生成二维码:{}",orders.getId(),downLineTrading.toString());
//        }
//        return downLineTrading;
//    }

    /**
     * 订单支付
     *
     * @param id              订单id
     * @param ordersPayReqDTO 订单支付请求体
     * @return 订单支付响应体
     */
    @Override
    public OrdersPayResDTO pay(Long id, OrdersPayReqDTO ordersPayReqDTO) {
        Orders orders =  baseMapper.selectById(id);
        if (ObjectUtil.isNull(orders)) {
            throw new CommonException( "订单不存在");
        }
        //订单的支付状态为成功直接返回
        if (OrderPayStatusEnum.PAY_SUCCESS.getStatus() == orders.getPayStatus()
                && ObjectUtil.isNotEmpty(orders.getTradingOrderNo())) {
            OrdersPayResDTO ordersPayResDTO = new OrdersPayResDTO();
            BeanUtil.copyProperties(orders, ordersPayResDTO);
            ordersPayResDTO.setProductOrderNo(orders.getId());
            return ordersPayResDTO;
        } else {
            //生成二维码
            NativePayResDTO nativePayResDTO = generateQrCode(orders, ordersPayReqDTO.getTradingChannel());
            OrdersPayResDTO ordersPayResDTO = BeanUtil.toBean(nativePayResDTO, OrdersPayResDTO.class);
            return ordersPayResDTO;
        }

    }

    //生成二维码
    private NativePayResDTO generateQrCode(Orders orders, PayChannelEnum tradingChannel) {

        //判断支付渠道
        Long enterpriseId = ObjectUtil.equal(PayChannelEnum.ALI_PAY, tradingChannel) ?
                tradeProperties.getAliEnterpriseId() : tradeProperties.getWechatEnterpriseId();

        //构建支付请求参数
        NativePayReqDTO nativePayReqDTO = new NativePayReqDTO();
        //商户号
        nativePayReqDTO.setEnterpriseId(enterpriseId);
        //业务系统标识
        nativePayReqDTO.setProductAppId("jzo2o.orders");
        //家政订单号
        nativePayReqDTO.setProductOrderNo(orders.getId());
        //支付渠道
        nativePayReqDTO.setTradingChannel(tradingChannel);
        //支付金额
        nativePayReqDTO.setTradingAmount(orders.getRealPayAmount());
        //备注信息
        nativePayReqDTO.setMemo(orders.getServeItemName());
        //判断是否切换支付渠道
        if (ObjectUtil.isNotEmpty(orders.getTradingChannel())
                && ObjectUtil.notEqual(orders.getTradingChannel(), tradingChannel.toString())) {
            nativePayReqDTO.setChangeChannel(true);
        }
        //生成支付二维码
        NativePayResDTO downLineTrading = nativePayApi.createDownLineTrading(nativePayReqDTO);
        if(ObjectUtils.isNotNull(downLineTrading)){
            log.info("订单:{}请求支付,生成二维码:{}",orders.getId(),downLineTrading.toString());
            //将二维码更新到交易订单中
            boolean update = lambdaUpdate()
                    .eq(Orders::getId, downLineTrading.getProductOrderNo())
                    .set(Orders::getTradingOrderNo, downLineTrading.getTradingOrderNo())
                    .set(Orders::getTradingChannel, downLineTrading.getTradingChannel())
                    .update();
            if(!update){
                throw new CommonException("订单:"+orders.getId()+"请求支付更新交易单号失败");
            }
        }
        return downLineTrading;
    }

    @Override
    public OrdersPayResDTO getPayResultFromTradServer(Long id) {
        //查一下订单
        Orders orders = baseMapper.selectById(id);
        //不存在就抛出异常
        if (ObjectUtils.isNull(orders)){
            throw new CommonException("订单不存在");
        }
        //支付结果
        Integer payStatus = orders.getPayStatus();
        //如果未支付才去请求支付服务拿支付结果
        if(payStatus==OrderPayStatusEnum.NO_PAY.getStatus()&&ObjectUtils.isNotEmpty(orders.getTradingOrderNo())){
            //拿到交易单号
            Long tradingOrderNo = orders.getTradingOrderNo();
            //根据交易单号请求支付服务的查询支付结果接口
            TradingResDTO tradResultByTradingOrderNo = tradingApi.findTradResultByTradingOrderNo(tradingOrderNo);
            //如果支付成功，更新数据库
            if (ObjectUtils.equal(tradResultByTradingOrderNo.getTradingState(), TradingStateEnum.YJS)){
                TradeStatusMsg tradeStatusMsg = new TradeStatusMsg();
                //订单id
                tradeStatusMsg.setProductOrderNo(orders.getId());
                //支付渠道
                tradeStatusMsg.setTradingChannel(tradResultByTradingOrderNo.getTradingChannel());
                //交易单号
                tradeStatusMsg.setTradingOrderNo(tradResultByTradingOrderNo.getTradingOrderNo());
                //微信交易单号（第三方的）
                tradeStatusMsg.setTransactionId(tradResultByTradingOrderNo.getTransactionId());
                //支付成功的code
                tradeStatusMsg.setStatusCode(TradingStateEnum.YJS.getCode());
                //支付成功的名称
                tradeStatusMsg.setStatusName(TradingStateEnum.YJS.getValue());
                //请求更新数据库
                owner.paySuccess(tradeStatusMsg);
                //构造返回的数据
                OrdersPayResDTO ordersPayResDTO = BeanUtils.toBean(tradeStatusMsg, OrdersPayResDTO.class);
                ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
                return  ordersPayResDTO;
            }

        }
        OrdersPayResDTO ordersPayResDTO = new OrdersPayResDTO();
        ordersPayResDTO.setPayStatus(payStatus);
        ordersPayResDTO.setProductOrderNo(orders.getId());
        ordersPayResDTO.setTradingOrderNo(orders.getTradingOrderNo());
        ordersPayResDTO.setTradingChannel(orders.getTradingChannel());
        return ordersPayResDTO;
    }

    @Override
    @Transactional
    public void paySuccess(TradeStatusMsg tradeStatusMsg) {
        //支付成功更新订单表，单独写一个service，因为有支付就会用，并且如果用户不点支付成功，使用消息队列，也要调用
//        boolean update = lambdaUpdate()
//                .eq(Orders::getId, tradeStatusMsg.getProductOrderNo())//订单id
//                .eq(Orders::getOrdersStatus,0)//订单支付状态只能由待支付变为派单中
//                .set(Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())//更新为支付成功
//                .set(Orders::getTransactionId, tradeStatusMsg.getTransactionId())
//                .set(Orders::getOrdersStatus, OrderStatusEnum.DISPATCHING.getStatus())//订单状态变为派单中
//                .set(Orders::getTradingOrderNo,tradeStatusMsg.getTradingOrderNo())//交易单号
//                .set(Orders::getTradingChannel,tradeStatusMsg.getTradingChannel())//支付渠道
//                .set(Orders::getPayTime, LocalDateTime.now())
//                .update();
//        if (!update){
//            throw new CommonException("支付成功更新订单状态："+tradeStatusMsg.getProductOrderNo()+"为派单中失败");
//        }

        //使用状态机将待支付变为派单中
        //Long dbShardId, String bizId, StatusChangeEvent statusChangeEventEnum, T bizSnapshot
        //查询订单
        Orders orders = baseMapper.selectById(tradeStatusMsg.getProductOrderNo());

        //2：待支付，4：支付成功
        if (ObjectUtil.notEqual(OrderPayStatusEnum.NO_PAY.getStatus(), orders.getPayStatus())) {
            log.info("当前订单：{}，不是待支付状态,当前状态为"+orders.getPayStatus()+"", orders.getId());
            return;
        }

        //第三方支付单号校验
        if (ObjectUtil.isEmpty(tradeStatusMsg.getTransactionId())) {
            throw new CommonException("支付成功通知缺少第三方支付单号");
        }
        // 修改订单状态和支付状态
        OrderSnapshotDTO orderSnapshotDTO = new OrderSnapshotDTO();
        orderSnapshotDTO.setTradingOrderNo(tradeStatusMsg.getTradingOrderNo());
        orderSnapshotDTO.setTradingChannel(tradeStatusMsg.getTradingChannel());
        orderSnapshotDTO.setPayTime(LocalDateTime.now());
        orderSnapshotDTO.setThirdOrderId(tradeStatusMsg.getTransactionId());
//        orderStateMachine.changeStatus( String.valueOf(orders.getId()), OrderStatusChangeEventEnum.PAYED, orderSnapshotDTO);
        //订单信息
        orderStateMachine.changeStatus(orders.getUserId(),tradeStatusMsg.getProductOrderNo().toString(), OrderStatusChangeEventEnum.PAYED,orderSnapshotDTO);


    }

    @Override
    public List<Orders> queryOverTimePayOrdersListByCount(Integer count) {
        //sql:select * from orders where orders_status =0 and create_time < 当前时间-15分钟
        List<Orders> list = lambdaQuery()
                .eq(Orders::getOrdersStatus, OrderStatusEnum.NO_PAY.getStatus())
                .lt(Orders::getCreateTime, LocalDateTime.now().minusMinutes(15))
                .last("limit " + count)
                .list();
        return list;
    }

    /*
    * 获取可用优惠券列表
    * */
    @Override
    public List<AvailableCouponsResDTO> getAvailableCoupons(Long serveId, Integer purNum) {
        //远程调用foundations
        ServeAggregationResDTO serveAggregationResDTO = serveApi.findById(serveId);
        if (ObjectUtils.isNull(serveAggregationResDTO)||serveAggregationResDTO.getSaleStatus()!=2){
            throw new CommonException("服务信息不可用");
        }
        BigDecimal price = serveAggregationResDTO.getPrice();
        //计算订单的总价
        BigDecimal totalAmount = price.multiply(new BigDecimal(purNum));
        //远程调用优惠券服务查询可用优惠券
        List<AvailableCouponsResDTO> available = marketClient.getAvailable(totalAmount);

        //返回可用优惠券列表
        return available;
    }

    //
    @Transactional(rollbackFor = Exception.class)
    public void add(Orders orders){
        //插入数据库
        boolean save = this.save(orders);
        if (!save) {
            throw new DbRuntimeException("下单失败");
        }
        //调用状态机的启动方法
        //参数：Long dbShardId 分库分表的时候要用, String bizId 订单id, T bizSnapshot 订单的快照
        OrderSnapshotDTO orderSnapshotDTO= BeanUtils.toBean(getById(orders.getId()), OrderSnapshotDTO.class);
        //分库是按用户id%3来分的
        orderStateMachine.start(orders.getUserId(),orders.getId().toString(),orderSnapshotDTO);
    }
}
